package com.example.sourcecodelearning.modules.redis;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.example.framework.common.util.ResponseUtils;
import com.example.sourcecodelearning.feign.UserFeign;
import com.example.sourcecodelearning.feign.dto.UserInfoRespDto;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;

/**
 * @author yuzhenxin
 * @date 2023-05-28 22:16
 */
@Slf4j
@Service
public class RedissonBloomFilterService {

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private UserFeign userFeign;

    private RBloomFilter<String> bloomFilter;

    private static final String FILTER_NAME = "userIdFilter";
    // 预测插入数量
    private static final long CAPACITY = 1000L;
    // 误判率
    private static final double ERROR_RATE = 0.01;

    @PostConstruct
    public void initData() {
        bloomFilter = redissonClient.getBloomFilter(FILTER_NAME);
        bloomFilter.tryInit(CAPACITY, ERROR_RATE);
        writeCache();
    }

    private void writeCache() {
        Set<String> ids = ResponseUtils.getRData(userFeign.getUserIds());
        if (ObjectUtil.isNotEmpty(ids)) {
            ids.forEach(o -> bloomFilter.add(o));
            log.debug("往布隆过滤器添加了{}", ids);

            String id1 = new ArrayList<>(ids).getFirst();
            UserInfoRespDto userInfo = ResponseUtils.getRData(userFeign.getUserByUserId(id1));
            redissonClient.getBucket(String.valueOf(id1))
                    .set(JSONUtil.toJsonStr(userInfo));
            log.debug("往Redis写入了缓存：【{}】-【{}】", id1, userInfo);
        }
    }

    public UserInfoRespDto getUserDOById(String id) {
        if (Objects.isNull(id)) {
            return null;
        }

        if (bloomFilter.contains(id)) {
            log.debug("布隆过滤器判断有{}", id);

            // 通过过滤器白名单，去Redis查询
            RBucket<String> bucket = redissonClient.getBucket(id);
            String valueStr = bucket.get();
            // redis查到了，直接返回
            if (Objects.nonNull(valueStr)) {
                UserInfoRespDto userInfo = JSONUtil.toBean(valueStr, UserInfoRespDto.class);
                log.info("从redis拿到了数据");
                return userInfo;
            }

            // redis查不到，去查数据库
            log.info("访问数据库了");
            UserInfoRespDto userInfo = ResponseUtils.getRData(userFeign.getUserByUserId(id));
            if (Objects.isNull(userInfo)) {
                log.debug("数据库查不到，返回");
                return null;
            } else {
                log.debug("数据库查到了，写入缓存");
                redissonClient.getBucket(id).set(JSONUtil.toJsonStr(userInfo));
            }
            return userInfo;
        } else {
            log.info("布隆过滤器拦截了");
            return null;
        }
    }
}
